# SimpleIdMapping.java

    package org.codefilarete.stalactite.mapping;

    import java.util.function.Function;

    import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
    import org.codefilarete.stalactite.engine.PersistExecutor.DefaultPersistExecutor.DefaultIsNewDeterminer;
    import org.codefilarete.stalactite.mapping.id.assembly.SingleIdentifierAssembler;
    import org.codefilarete.stalactite.mapping.id.manager.AlreadyAssignedIdentifierManager;
    import org.codefilarete.stalactite.mapping.id.manager.IdentifierInsertionManager;
    import org.codefilarete.stalactite.sql.ddl.structure.Table;

    /**
     * Entry point for single value (hence single-column primary key), as opposed to composed, about entity identifier mapping.
     * Will mainly delegate its work to an {@link IdAccessor}, an {@link IdentifierInsertionManager} and a {@link SingleIdentifierAssembler}
     *
     * @author Guillaume Mary
     * @see ComposedIdMapping
     */
    public class SimpleIdMapping<C, I> implements IdMapping<C, I> {

    	private final AccessorWrapperIdAccessor<C, I> idAccessor;

    	private final IdentifierInsertionManager<C, I> identifierInsertionManager;

    	private final DefaultIsNewDeterminer<C> isNewDeterminer;

    	private final SingleIdentifierAssembler<I, ?> identifierMarshaller;

    	/**
    	 * Main constructor
    	 *
    	 * @param idAccessor entry point to get/set id of an entity
    	 * @param identifierInsertionManager defines the way the id is persisted into the database
    	 * @param identifierMarshaller defines the way the id is read from the database
    	 */
    	public SimpleIdMapping(AccessorWrapperIdAccessor<C, I> idAccessor,
    						   IdentifierInsertionManager<C, I> identifierInsertionManager,
    						   SingleIdentifierAssembler<I, ?> identifierMarshaller) {
    		this.idAccessor = idAccessor;
    		this.identifierInsertionManager = identifierInsertionManager;
    		this.identifierMarshaller = identifierMarshaller;
    		if (identifierInsertionManager instanceof AlreadyAssignedIdentifierManager) {
    			this.isNewDeterminer = new AlreadyAssignedIdDeterminer(((AlreadyAssignedIdentifierManager<C, I>) identifierInsertionManager).getIsPersistedFunction());
    		} else if (identifierInsertionManager.getIdentifierType().isPrimitive()) {
    			this.isNewDeterminer = new PrimitiveIdDeterminer();
    		} else {
    			this.isNewDeterminer = new NullableIdDeterminer();
    		}
    	}

    	public SimpleIdMapping(ReadWritePropertyAccessPoint<C, I> identifierAccessor,
    						   IdentifierInsertionManager<C, I> identifierInsertionManager,
    						   SingleIdentifierAssembler<I, ?> identifierMarshaller) {
    		this(new AccessorWrapperIdAccessor<>(identifierAccessor), identifierInsertionManager, identifierMarshaller);
    	}

    	@Override
    	public AccessorWrapperIdAccessor<C, I> getIdAccessor() {
    		return idAccessor;
    	}

    	@Override
    	public IdentifierInsertionManager<C, I> getIdentifierInsertionManager() {
    		return identifierInsertionManager;
    	}

    	@Override
    	public boolean isNew(C entity) {
    		return isNewDeterminer.isNew(entity);
    	}

    	@Override
    	public <T extends Table<T>> SingleIdentifierAssembler<I, T> getIdentifierAssembler() {
    		return (SingleIdentifierAssembler<I, T>) identifierMarshaller;
    	}

    	/**
    	 * For case where the identifier is a basic type (String, Long, ...)
    	 */
    	private class NullableIdDeterminer extends DefaultIsNewDeterminer<C> {

    		@Override
    		public boolean isNew(C entity) {
    			return idAccessor.getId(entity) == null;
    		}
    	}

    	/**
    	 * For case where the identifier is a primitive type (long, int, ...)
    	 */
    	private class PrimitiveIdDeterminer extends DefaultIsNewDeterminer<C> {

    		@Override
    		public boolean isNew(C entity) {
    			return ((Number) idAccessor.getId(entity)).intValue() == 0;
    		}
    	}

    	/**
    	 * For case where the identifier is already assigned : we have to delegate determination to a function
    	 */
    	private class AlreadyAssignedIdDeterminer extends DefaultIsNewDeterminer<C> {

    		private final Function<C, Boolean> isPersistedFunction;

    		private AlreadyAssignedIdDeterminer(Function<C, Boolean> isPersistedFunction) {
    			this.isPersistedFunction = isPersistedFunction;
    		}

    		@Override
    		public boolean isNew(C entity) {
    			return !isPersistedFunction.apply(entity);
    		}
    	}
    }